package com.tlvip.design.singleton.innerAttack;

import java.io.*;

/**
 * <p>description：</p>
 * <p>copyright： copyright(C)2016-2099</p>
 * <p>Life is short,we need passion</p>
 * <p>Summary： </p>
 * <p>instructions： </p>
 * Date 2021/8/15</p>
 * Author mac
 *
 * @version 1.0
 */
public class InnerClassSingletonAttack {
    public static void main(String[] args) {
//        try {
//            InnerSingleton instance = InnerSingleton.getInstance();
//            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("testSerializable"));
//            oos.writeObject(instance);
//            oos.close();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }

        InnerSingleton instance = InnerSingleton.getInstance();

        try {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("testSerializable"));
            // 遇到对象实现了readResolve()方法，则调用此方法获取对象
            InnerSingleton object = (InnerSingleton) ois.readObject();
            // false，jvm序列化不会产生相同的对象，
            // 参照
            System.out.println(instance == object);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }


    }
}

// 静态内部类懒加载实现
class InnerSingleton implements Serializable {

    private static final long serialVersionUID = 8831294799931832294L;

    private static class InnerClassHolder {
        private static InnerSingleton innerSingleton = new InnerSingleton();
    }

    // jvm序列化时，直接从字节流中取数据，不会调用构造方法，
    // 如果想让jvm反序列化时，检查对象是否初始化，则需要参照Serializable提示，
    // 定义readResolve()并从已有的方法中获取实例
    private InnerSingleton() {
        // 防止反射方式攻击，单例模式不允许多个实例
        if (InnerClassHolder.innerSingleton != null) {
            throw new RuntimeException("单例模式不允许多个实例存在");
        }
    }

    // 本质 懒加载 ，使用类加载机制保证线程安全性
    public static InnerSingleton getInstance() {
        // 当调用内部类静态属性时，触发内部类InnerClassHolder初始化，从而触发对象实例化
        return InnerClassHolder.innerSingleton;
    }

    // 此方法解决从磁盘实例化对象不能是单实例的问题
    Object readResolve() throws ObjectStreamException {
        // 不指定serialVersionUID会报错，
        return InnerClassHolder.innerSingleton;
    }

    // 即使增加一个字段，版本号serialVersionUID不改变的情况下，从磁盘加载的类仍是单例模式
    float ff = 0.99f;
}